﻿using RedLockNet.SERedis;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Tsual.DataPipeline;

namespace Tsual.DataPipeline.Redis
{
    public class RedisStringPipe<TMeta> : AbstractRedisPipe<string, TMeta>
        where TMeta : IConvertible<string> , IConvertible<LockType>
    {
        public RedisStringPipe(IDatabase cache, RedLockFactory lockFactory) : base(cache, lockFactory)
        {
        }

        public override bool CanRead(TMeta meta) => meta != null;

        public override bool CanWrite(string data, TMeta meta) => meta != null;

        public override Task<DataPipeReadResult<string>> ReadCache(TMeta meta) => Task.Run(() =>
        {
            var key = ((IConvertible<string>)meta).Convert();
            return cache.KeyExists(key) ? DataPipeReadResult<string>.Success(cache.StringGet(key)) : DataPipeReadResult<string>.Notfound();
        });

        public override Task<DataPipeWriteResult> WriteCache(string data, TMeta meta) => Task.Run(() =>
        {
            var key = ((IConvertible<string>)meta).Convert();
            cache.StringSet(key, data);
            return DataPipeWriteResult.Success();
        });

        protected override string GetReadLockKey(TMeta meta) => ((IConvertible<string>)meta).Convert();
    }

    public class RedisExpirableStringPipe<TMeta> : AbstractRedisPipe<ExpirablePipeValue<string>, TMeta>
        where TMeta : IConvertible<string>, IConvertible<LockType>
    {
        public RedisExpirableStringPipe(IDatabase cache, RedLockFactory lockFactory) : base(cache, lockFactory)
        {
        }

        public override bool CanRead(TMeta meta) => meta != null;

        public override bool CanWrite(ExpirablePipeValue<string> data, TMeta meta) => meta != null;

        public override Task<DataPipeReadResult<ExpirablePipeValue<string>>> ReadCache(TMeta meta) => Task.Run(() =>
        {
            var key = ((IConvertible<string>)meta).Convert();
            if (!cache.KeyExists(key)) return DataPipeReadResult<ExpirablePipeValue<string>>.Notfound();
            var rtn = new ExpirablePipeValue<string>()
            {
                Value = cache.StringGet(key),
            };
            var exp = cache.KeyTimeToLive(key);
            if (exp.HasValue) rtn.AbsoluteExpireTime = DateTimeOffset.Now.Add(exp.Value);
            return DataPipeReadResult<ExpirablePipeValue<string>>.Success(rtn);
        });

        public override Task<DataPipeWriteResult> WriteCache(ExpirablePipeValue<string> data, TMeta meta) => Task.Run(() =>
        {
            var key = ((IConvertible<string>)meta).Convert();
            if (data.AbsoluteExpireTime.HasValue)
            {
                cache.StringSet(key, data.Value, data.AbsoluteExpireTime.Value - DateTimeOffset.Now);
            }
            else
            {
                cache.StringSet(key, data.Value);
            }
            return DataPipeWriteResult.Success();
        });

        protected override string GetReadLockKey(TMeta meta) => ((IConvertible<string>)meta).Convert();
    }
}
